
const _ = require('lodash')

// 函数组合演示
// 洋葱代码并没有被省略，而是被封装起来了
// function compose(f, g) {
//   return function (value) {
//     return f(g(value))
//   }
// }
// 实现将数组先反转在取第一个
// function reverse(arr) {
//   return arr.reverse()
// }
// function first(arr) {
//   return arr[0]
// }
// const last = compose(first, reverse)
// console.log(last([1, 2, 3, 4, 5]))  //5


//  使用lodash 中的函数组合的方法： _.flowRight()
// //  实现先翻转，再去第一个，再大写
// const reverse = arr => arr.reverse()
// const first = arr => arr[0]
// const toUpper = s => s.toUpperCase()

// const f = _.flowRight(toUpper, first, reverse)
// const f = flowRight(toUpper, first, reverse)
// const f = compose(toUpper, first, reverse)
// console.log(f(['one', 'two', 'three']))



// 自己实现flowRight
// function flowRight(...args) {
//   return function (value) {
//     let fn = args[args.length - 1](value);
//     if (args.length > 1) {
//       for (let i = args.length - 2; i >= 0; i--) {
//         fn = args[i](fn)
//       }
//     }
//     return fn
//   }
// }

// function compose(...args) {
//   return function (value) {
//     return args.reverse().reduce((pre, pro) => {
//       return pro(pre)
//     }, value)
//   }
// }

// const compose = (...args) => (value => (args.reverse().reduce((pre, pro) => pro(pre), value)))
// 改为箭头函数
// const compose = (...args) => value => args.reverse().reduce((pre, pro) => pro(pre), value)
// const f = compose(toUpper, first, reverse)
// console.log(f(['one', 'two', 'three']))


// 函数组合-结合律
// const f = _.flowRight(_.toUpper, _.first, _.reverse)
// const f = _.flowRight(_.flowRight(_.toUpper, _.first), _.reverse)
// const f = _.flowRight(_.toUpper, _.flowRight(_.first, _.reverse))
// console.log(f(['one', 'two', 'three']))


// 函数组合-调试
// NEVER SAY DIE  -->  never-say-die
// const log = (v) => {
//   console.log(v)
//   return v
// }
// const log = _.curry((tag, v) => {
//   console.log(tag, v)
//   return v
// })
// // _.toLower()
// // _.split()
// const split = _.curry((sep, str) => _.split(str, sep))
// // _.join()
// const join = _.curry((sep, arr) => _.join(arr, sep))

// const f = _.flowRight(join('-'), log('lower 后'), _.toLower, log('split 后'), split(' '))
// split 后 [ 'NEVER', 'SAY', 'DIE' ]
// lower 后 never,say,die

// const f = _.flowRight(log('join 后'), join('-'), log('split 后'), split(' '), log('lower 后'), _.toLower)
// f('NEVER SAY DIE')
// console.log(f('NEVER SAY DIE'))



// lodash/fp
// lodash 模块,  都是数据优先，函数滞后
_.map(['a', 'b', 'c'], _.toUpper) // ['A','B','C']
_.map(['a', 'b', 'c']) // ['a', 'b', 'c']

_.split('Hello World', ' ')

// lodash/fp 模块,  都是函数优先，数组滞后
const fp = require('lodash/fp')

fp.map(fp.toUpper, ['a', 'b', 'c'])
fp.map(fp.toUpper)(['a', 'b', 'c'])

fp.split(' ', 'Hello World')
fp.split(' ')('Hello World')


// const f = fp.flowRight(fp.join('-'), fp.map(fp.toLower), fp.split(' '))
// const f = fp.flowRight(fp.join('-'), fp.split(' '), fp.toLower)
// console.log(f('NEVER SAY DIE'))  // never-say-die


// lodash map   vs   lodash/fp map

// console.log(_.map(['23', '8', '10'], parseInt))  // [ 23, NaN, 2 ]
// parseInt('23', 0, array)
// parseInt('8', 1, array)
// parseInt('10', 2, array)

// console.log(fp.map(parseInt, ['23', '8', '10']))  //[ 23, 8, 10 ]



// point free
// Hello      Word => hello_world
// fp 中的方法都是柯里化的
// fp.replace可以接受3个参数，1 匹配的正则，  2 替换成什么  3 要修改的字符串
// fp.replace是柯里化所以接收两个参数可以返回一个函数，这个函数可以接受一个字符串
// const f = fp.flowRight(fp.replace(/\s+/g, '_'), fp.toLower)
// console.log(f('Hello      Word')) // hello_word


// 把一个字符串中的首字母提取，转换成大写，使用. 作为分隔符
// world wild web ==> W. W. W
// const f = fp.flowRight(fp.toUpper, fp.join('. '), fp.map(fp.first), fp.split(' '), fp.replace(/\s+/g, ' '))
// // const f = fp.flow(fp.replace(/\s+/g, ' '), fp.split(' '), fp.map(fp.first), fp.join('. '), fp.toUpper)
// console.log(f('world  wild  web')) // W. W. W





// Functor 函子
// class Container {
//   constructor(value) {
//     // 维护一个不对外公布的静态变量
//     this._value = value
//   }
//   // map方法通过fn处理值，并通过新的函子返回
//   map(fn) {
//     return new Container(fn(this._value))
//   }
// }
// // map方法返回的是新的函子，所以还可以调用map方法
// const lr = new Container(5)
//   .map(v => v + 3)
//   .map(v => v * 2)

// console.log(lr)  // Container { _value: 16 }

// class Container {
//   static of(value) {
//     return new Container(value)
//   }
//   constructor(value) {
//     // 维护一个不对外公布的静态变量
//     this._value = value
//   }
//   // map方法通过fn处理值，并通过新的函子返回
//   map(fn) {
//     return Container.of(fn(this._value))
//   }
// }
// // map方法返回的是新的函子，所以还可以调用map方法
// const lr = Container.of(5)
//   .map(v => v + 3)
//   .map(v => v * 2)

// console.log(lr)  // Container { _value: 16 }





// MayBe 函子
// class MayBe {
//   static of(value) {
//     return new MayBe(value)
//   }
//   constructor(value) {
//     this._value = value
//   }
//   // 通过 isNothing 判断是否为空值
//   map(fn) {
//     return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
//   }
//   isNothing() {
//     return this._value === null || this._value === undefined
//   }
// }

// let r = MayBe.of(null)
//   .map(x => x.toUpperCase())
// let r = MayBe.of('hello world')
//   .map(x => x.toUpperCase())
//   .map(x => null)
//   .map(x=> x.toUpperCase())
// console.log(r)  // MayBe { _value: null }





// Either
// class Left {
//   static of(value) {
//     return new Left(value)
//   }
//   constructor(value) {
//     this._value = value
//   }
//   map(fn) {
//     return this
//   }
// }

// class Right {
//   static of(value) {
//     return new Right(value)
//   }
//   constructor(value) {
//     this._value = value
//   }
//   map(fn) {
//     return Right.of(fn(this._value))
//   }
// }

// let r1 = Right.of(12).map(x => x + 2)
// let r2 = Left.of(12).map(x => x + 2)
// console.log(r1)
// console.log(r2)

// function parseJson(str) {
//   try {
//     return Right.of(JSON.parse(str))
//   } catch (e) {
//     return Left.of({ error: e.message })
//   }
// }

// let r1 = parseJson('{name:zs}')
// console.log(r1)  // Left { _value: { error: 'Unexpected token n in JSON at position 1' } }
// let r2 = parseJson('{"name":"zs"}')
//   .map(x => x.name.toUpperCase())
// console.log(r2) // Right { _value: 'ZS' }



// // IO 函子
// class IO {
//   static of(value) {
//     // this._value 就是这里的function
//     return new IO(function () {
//       return value
//     })
//   }
//   constructor(fn) {
//     this._value = fn
//   }
//   map(fn) {
//     return new IO(fp.flowRight(fn, this._value))
//   }
// }
// // process - node 进程
// let r = IO.of(process).map(p => p.execPath)
// console.log(r) // IO { _value: [Function (anonymous)] }
// console.log(r._value())  // D:\node.js\node.exe



// folktale 中的 compose, curry
const { compose, curry } = require('folktale/core/lambda')
const { toUpper, first } = require('lodash/fp')
// let f = curry(2, (x, y) => {
//   return x + y
// })
// console.log(f(1, 2)) // 3
// console.log(f(1)(2))// 3

// let n = compose(toUpper, first)
// console.log(n(['one', 'two'])) // ONE


// Task 处理异步任务
// const fs = require('fs')
// const { task } = require('folktale/concurrency/task')
// const { split, find } = require('lodash/fp')
// function readFile(filename) {
//   return task(resolver => {
//     fs.readFile(filename, 'utf-8', (err, data) => {
//       if (err) resolver.reject(er)
//       resolver.resolve(data)
//     })
//   })
// }
// readFile('../../../package.json')
//   .map(split('\n'))
//   .map(find(x => x.includes('version')))
//   .run()
//   .listen({
//     onRejected: err => {
//       console.log(err)
//     },
//     onResolved: value => {
//       console.log(value)
//     }
//   })


// IO函子的问题
// class IO {
//   static of(value) {
//     // this._value 就是这里的function
//     return new IO(function () {
//       return value
//     })
//   }
//   constructor(fn) {
//     this._value = fn
//   }
//   map(fn) {
//     return new IO(fp.flowRight(fn, this._value))
//   }
// }
// const fs = require('fs')
// let readFile = function (filename){
//   return new IO(function(){
//     return fs.readFileSync(filename, 'utf-8')
//   })
// }
// let print = function(x){
//   return new IO(function(){
//     console.log(x)
//     return x
//   })
// }
// let cat = fp.flowRight(print, readFile)
// // IO(IO(x))
// // let r = cat('../../../package.json')._value()  // IO { _value: [Function (anonymous)] }
// let r = cat('../../../package.json')._value()._value()
// console.log(r)


//  IO Monad
class IO {
  static of(value) {
    return new IO(function () {
      return value
    })
  }
  constructor(fn) {
    this._value = fn
  }
  map(fn) {
    return new IO(fp.flowRight(fn, this._value))
  }
  join() {
    return this._value()
  }
  flatMap(fn) {
    return this.map(fn).join()
  }
}
const fs = require('fs')
let readFile = function (filename) {
  return new IO(function () {
    return fs.readFileSync(filename, 'utf-8')
  })
}
let print = function (x) {
  return new IO(function () {
    console.log(x)
    return x
  })
}
let r = readFile('../../../package.json')
  .map(fp.toUpper)
  .flatMap(print)
  .join()
console.log(r)